/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://gitee.com/newgolo/embedme.git
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include "BaseType.h"
#include "Tracer.h"
#include "STPPacker.h"
#include "StrUtil.h"
#include "CRCCheck.h"

#define STP_FRAME_HEAD      "\xFE\xFE"  /* 帧起始标志 */
#define STP_DATA_ESCAPE     "\xFE\xFE\xFE\xFE"
#define STP_FRAMESIZE_MAX   0x8000      /* 帧最大长度 */
#define STP_FRAMESIZE_MIN   0x0006      /* 帧最小长度 */
#define STP_CRC_TYPE        CRC_TYPE_CRC16_3D65

namespace libembx{
typedef enum{
    STP_STATE_NOFRAME=0,  /* 未找到帧头 */
    STP_STATE_FRAMEHEAD,  /* 已找到帧头 */
    STP_STATE_FRAMESIZE,  /* 已找到帧长度 */
    /* 根据帧长度取出剩余帧数据，然后回到STP_STATE_NOFRAME状态 */
}STP_STATE_E;

STPPacker::STPPacker()
{
    m_dataBuffer = "";
    m_stpState = STP_STATE_NOFRAME;
}

STPPacker::~STPPacker()
{
}

/**
 *  @brief  数据包解包
 *  @param  buf  接收到的字串
 *  @param  size buf的长度
 *  @return 返回解包好的数据包
 *  @note   协议格式:帧标识(FEFE)+帧长度(2Bytes,不大于0x8000,不小于0x0006)+消息内容(payload)+CRC校验码(2Bytes,校验payload)
 *          协议采用大端字节序(低地址放高位,高地址放低位)
 */
std::vector<std::string> STPPacker::unpackOn(const char* buf, int size)
{
    std::vector<std::string> result;
    result.clear();
    std::string unpackStr(buf,size);
    if (unpackStr.empty()) 
    {
        return result;
    }
    if (m_dataBuffer.size() >= STP_FRAMESIZE_MAX) 
    {
        TRACE_WARN_CLASS("data buffer is full,reset it!\n");
        m_dataBuffer="";
    }
    m_dataBuffer.append(unpackStr);
    //TRACE_HEX("buffer111",m_dataBuffer.c_str(),m_dataBuffer.size());
    while (1) 
    {
        if(m_stpState==STP_STATE_NOFRAME)
        {
            m_frameStart = -1;
            m_frameSize = 0;
            int pos = m_dataBuffer.find(STP_FRAME_HEAD,0);
            if (pos==std::string::npos)/* 未找到帧头标识,直接退出 */
            {
                break;
            }
            if (m_dataBuffer.substr(pos,4)==STP_DATA_ESCAPE)/* 是转义字符,继续搜索 */
            {
                m_dataBuffer = m_dataBuffer.substr(pos+4);
                continue;
            }
            /* 找到帧头 */
            m_frameStart = pos;
            m_stpState = STP_STATE_FRAMEHEAD;
            continue;   
        }
        else if(m_stpState==STP_STATE_FRAMEHEAD)
        {
            int pos = m_frameStart+4;
            if (pos > m_dataBuffer.size()) /* 没有消息长度字段,退出循环 */
            {
                break;
            }
            /* 协议为大端字节序 */
            uint16 frameSize = (uint16)(m_dataBuffer[m_frameStart+2]);
            frameSize = (frameSize<<8)+ (uint16)(m_dataBuffer[m_frameStart+3]);
            if (frameSize<=STP_FRAMESIZE_MIN || frameSize>STP_FRAMESIZE_MAX) /* 长度不满足要求 */
            {
                m_dataBuffer = m_dataBuffer.substr(pos);
                m_stpState = STP_STATE_NOFRAME;
                continue;
            }
            /* 获取到帧长度 */
            m_frameSize = frameSize;
            m_stpState = STP_STATE_FRAMESIZE;
            continue;
        }
        else if(m_stpState == STP_STATE_FRAMESIZE)
        {
            if (m_dataBuffer.size() - m_frameStart >= m_frameSize) 
            {
                std::string frame = m_dataBuffer.substr(m_frameStart,m_frameSize);
                std::string payload = getPayload(frame);/* 获取payload数据 */
                if (!payload.empty()) 
                {
                    result.push_back(payload);
                }
                m_dataBuffer = m_dataBuffer.substr(m_frameStart+m_frameSize);
                m_stpState = STP_STATE_NOFRAME;
                continue;
            }
            /* 帧数据未到达结尾,退出循环,等下一次填入数据 */
            break;
        }
        else
        {
            break;
        }
    }
    return result;
}

std::string STPPacker::packData(const char* data, int size)
{
    if (data==NULL || size <=0) 
    {
        TRACE_ERR_CLASS("Invalid parameter!\n");
        return "";
    }
    std::string frame = STP_FRAME_HEAD;
    std::string frameData(data,size);
    CRCCheck crc;
    uint16 crcCode;
    if(!crc.doCRC16Check(frameData, STP_CRC_TYPE,crcCode))
    {
        TRACE_ERR_CLASS("CRC Check error!\n");
        return "";
    }
    uint8 high = (uint8)(crcCode >> 8);
    uint8 low = (uint8)(crcCode&0x00FF);
    frameData.append(1,(char)high);
    frameData.append(1,(char)low);
    frameData = StrUtil::replaceString(frameData, STP_FRAME_HEAD, STP_DATA_ESCAPE);
    uint16 frameSize = frameData.size()+4;
    if (frameSize<=STP_FRAMESIZE_MIN ||
        frameSize > STP_FRAMESIZE_MAX) 
    {
        TRACE_ERR_CLASS("frame size error:%d!\n",frameSize);
        return "";
    }
    high = (uint8)(frameSize>>8);
    low = (uint8)(frameSize&0x00FF);
    frame.append(1,(char)high); 
    frame.append(1,(char)low);
    return frame.append(frameData);
}


std::string STPPacker::getPayload(const std::string& frame)
{
    /* 协议格式:帧标识(FEFE)+帧长度(2Bytes,不大于0x8000)+消息内容+CRC校验码(2Bytes) */
    int frameSize = frame.size();
    std::string frameData = frame.substr(4);
    frameData = StrUtil::replaceString(frameData, STP_DATA_ESCAPE, STP_FRAME_HEAD);
    /* 计算CRC */
    CRCCheck crc;
    uint16 crcCode;
    int payloadLen = frameData.size()-2;
    if(!crc.doCRC16Check(frameData, STP_CRC_TYPE, crcCode))
    {
        TRACE_ERR_CLASS("CRC Check error!\n");
        return "";
    }
    uint8 high = (uint8)(crcCode >> 8);
    uint8 low = (uint8)(crcCode&0x00FF);
    if (high!=(uint8)frameData[payloadLen] ||
        low !=(uint8)frameData[payloadLen+1]) 
    {
        TRACE_ERR_CLASS("CRC Check error:%02X%02X=>%02X%02X!\n",
                        (uint8)frameData[payloadLen],(uint8)frameData[payloadLen+1],
                        high,low);
        return "";
    }
    return frameData.substr(0,payloadLen);
}
}

